using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace Team33Assignment1
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class World : Microsoft.Xna.Framework.DrawableGameComponent
    {

        private Player player;
        private Camera camera;
        private Skybox skybox;
        private Plane plane;
        private GraphicsDeviceManager graphics;
        private List<Enemy> enemies;
        private List<Chest> chests;
        private ContentManager content;

        SoundEffect soundeffect;

        public MouseState prevMouseState;
        public MouseState currentMouseState;
        public KeyboardState prevKeyboardState;
        public KeyboardState currentKeyboardState;

        int score = 0;
        int deaths = 0;

        SpriteBatch foreground;
        SpriteFont font;

        static Random rand = new Random();

        public World(Game game, GraphicsDeviceManager graphics, Player player, Camera camera, SpriteBatch sprite)
            : base(game)
        {
            this.player = player;
            this.camera = camera;
            this.graphics = graphics;
            this.skybox = new Skybox(Game.Content);
            this.plane = new Plane(Game.Content);
            this.enemies = new List<Enemy>();
            this.chests = new List<Chest>();
            soundeffect = Game.Content.Load<SoundEffect>(@"sounds\pew");

            foreground = sprite;
            font = Game.Content.Load<SpriteFont>(@"font");

            SetupEnemies(Game.Content);
            SetupChests(Game.Content);

            content = Game.Content;
            
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            // TODO: Add your initialization code here
            // Set mouse position and do initial get state
            Mouse.SetPosition(Game.Window.ClientBounds.Width / 2,
                Game.Window.ClientBounds.Height / 2);
            prevMouseState = Mouse.GetState();

            

            base.Initialize();
        }

        protected override void LoadContent()
        {

            

            base.LoadContent();
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // TODO: Add your update code here
            UpdateEnemies(gameTime);
            UpdateChests(gameTime);
            UpdateInput();
            Inputs();

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game component should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Draw(GameTime gameTime)
        {

            Matrix world = Matrix.CreateRotationY(player.yaw) * Matrix.CreateTranslation(player.position);
            
            skybox.Draw(graphics.GraphicsDevice, camera.position, camera.view, camera.projection);

            plane.Draw(graphics.GraphicsDevice, Vector3.Zero, camera.view, camera.projection);
            
            player.Draw(world, camera.view, camera.projection);

            DrawEnemies(Matrix.Identity, camera.view, camera.projection);
            DrawChests(Matrix.Identity, camera.view, camera.projection);

            DrawScore();

            base.Draw(gameTime);
        }

        private void SetupEnemies(ContentManager content)
        {
            enemies = new List<Enemy>();
            
            for (int i = 0; i < 1; i++)
            {
                float x = rand.Next() % 500.0f;
                float z = rand.Next() % 500.0f;
                Vector3 pos = new Vector3(x, 0, z);
                Model tmpModel = content.Load<Model>(@"enemy\enemySphere");
                Enemy tmp = new Enemy(tmpModel, pos, player, i);
                enemies.Add(tmp);
            }
        }

        private void SetupChests(ContentManager content)
        {
            chests = new List<Chest>();

            for (int i = 0; i < 1; i++)
            {
                float x = rand.Next() % 100.0f;
                float z = rand.Next() % 100.0f;
                Vector3 pos = new Vector3(x, 0, z);
                Model tmpModel = content.Load<Model>(@"chest\goalSphere");
                Chest tmp = new Chest(tmpModel, pos, player);
                chests.Add(tmp);
                
            }
        }

        private void DrawEnemies(Matrix world, Matrix view, Matrix projection)
        {
            foreach (Enemy em in enemies)
            {
                Matrix tmp = Matrix.CreateTranslation(em.position);
                em.Draw(tmp, view, projection);
            }
        }

        private void DrawChests(Matrix world, Matrix view, Matrix projection)
        {
            foreach (Chest ch in chests)
            {
                Matrix tmp = Matrix.CreateTranslation(ch.position);
                ch.Draw(tmp, view, projection);
            }
        }

        private void Inputs()
        {
            if (currentKeyboardState.IsKeyDown(Keys.A))
                player.yaw += player.rotationSpeed;

            if (currentKeyboardState.IsKeyDown(Keys.D))
                player.yaw -= player.rotationSpeed;

            if (currentKeyboardState.IsKeyDown(Keys.W))
            {
                Matrix forwardMovement = Matrix.CreateRotationY(player.yaw);
                Vector3 v = new Vector3(0, 0, player.forwardSpeed);
                v = Vector3.Transform(v, forwardMovement);
                Vector3 oldPos = player.position - new Vector3(-0.15f, 0, -0.15f);
                Vector3 pos = player.position;
                pos.Z += v.Z;
                pos.X += v.X;

                if (CheckForCollisions())
                {
                    pos = oldPos;
                    deaths += 1;
                    Reset();
                }

                player.setPosition(pos);
            }

            if (currentKeyboardState.IsKeyDown(Keys.S))
            {
                Matrix forwardMovement = Matrix.CreateRotationY(player.yaw);
                Vector3 v = new Vector3(0, 0, -player.forwardSpeed);
                v = Vector3.Transform(v, forwardMovement);
                Vector3 oldPos = player.position - new Vector3(-1, 0, -1);
                Vector3 pos = player.position;
                pos.Z += v.Z;
                pos.X += v.X;

                if (CheckForCollisions())
                {
                    pos = oldPos;
                    deaths += 1;
                    Reset();
                }

                player.setPosition(pos);
            }

            if ((currentKeyboardState.IsKeyDown(Keys.Space) && prevKeyboardState.IsKeyUp(Keys.Space)) ||
                (currentMouseState.LeftButton == ButtonState.Pressed && prevMouseState.LeftButton != ButtonState.Pressed))
            {
                soundeffect.Play();
            }
        }

        private void Reset()
        {
            SetupEnemies(content);
            SetupChests(content);
        }

        private bool CheckForCollisions()
        {
            foreach (Enemy em in enemies)
            {
                if (CheckForCollision(player, em, 20f, 15f))
                    return true;
            }
            return false;
        }

        private bool CheckForCollision(GameObject c1, GameObject c2, float c1Radius, float c2Radius)
        {
            BoundingSphere object1Sphere = new BoundingSphere(c1.position, c1Radius);
            BoundingSphere object2Sphere = new BoundingSphere(c2.position, c2Radius);
            if (object1Sphere.Intersects(object2Sphere))
            {
                return true;
            }
            return false;
        }

        private void UpdateEnemies(GameTime gameTime)
        {
            foreach (Enemy em in enemies)
            {
                bool collided = false;
                foreach (Enemy em2 in enemies)
                {
                    if (em.getId() != em2.getId())
                    {
                        if (CheckForCollision(em, em2, 15f, 15f))
                            collided = true;
                    }
                }
                em.Update(collided);
            }
        }

        private void UpdateChests(GameTime gameTime)
        {
            Chest tmp = null;
            bool win = false;
            foreach (Chest ch in chests)
            {
                ch.Update(gameTime);
                if (CheckForCollision(ch, player, 15f, 20f))
                {
                    tmp = ch;//Destroy chest here
                    score += 100;
                    win = true;
                }
            }

            removeChest(tmp);

            if (win)
                Reset();
        }

        private void removeChest(Chest chest)
        {
            chests.Remove(chest);
        }

        private void UpdateInput()
        {
            // Update these variables
            prevMouseState = currentMouseState;
            currentMouseState = Mouse.GetState();
            prevKeyboardState = currentKeyboardState;
            currentKeyboardState = Keyboard.GetState();

            Rectangle clientBounds = Game.Window.ClientBounds;
            int centerX = clientBounds.Width / 2;
            int centerY = clientBounds.Height / 2;
            Mouse.SetPosition(centerX, centerY);
        }

        private void DrawScore()
        {
            foreground.Begin();

            Vector2 fontPos = new Vector2(50, 50);

            String msg = "Score: " + score.ToString() + "\nDeaths: " + deaths.ToString();
            // Draw the string.

            foreground.DrawString(font, msg, fontPos, Color.Black);

            foreground.End();
        }
    }
}
